Einheit 4 — Die Syntax des Workflow-Quelltexts
Was du nach dieser Einheit weißt: Du kannst einen Workflow vollständig als Text schreiben — Module, Flows, Nodes, Optionen, Verbindungen — und kennst die Stellen, an denen die Syntax sich anders verhält, als man erwartet.
Der Quelltext ist klein. Es gibt vier Bausteine, und du hast sie in zehn Minuten gesehen. Die Tücke liegt nicht im Umfang, sondern in einer Handvoll Sonderfälle, die ohne Fehlermeldung das Falsche tun. Die stehen deshalb alle in dieser Einheit.
Das Grundgerüst
module academy
flow kontaktanfrage {
node eingang: form {
secret: "academy-kurs6"
}
node extrahieren: ai {
model: "gus:tav"
}
eingang -> extrahieren
}
module
Jede Datei beginnt mit einer Modulzeile. Fehlt sie, bricht der Compiler mit missing module declaration ab. Der Name ist ein Bezeichner — Buchstaben, Ziffern, Unterstriche, keine Anführungszeichen.
flow
Ein flow-Block umfasst einen Workflow. Auch der Flow-Name ist ein Bezeichner, kein String:
flow kontaktanfrage { … } // richtig
flow "Kontaktanfrage" { … } // Syntaxfehler
Der Name im Quelltext taucht in 42°flow nirgends auf. Den sichtbaren Workflow-Namen setzt du über den Parameter name beim Deployen.
Rufst du workflow_update ohne name auf, wird der Workflow in der Oberfläche in „Workflow" umbenannt — und eine nicht mitgegebene description wird geleert. Gib beide bei jedem Update mit an.
node
node <name>: <typ> {
option: wert
}
Der Node-Name wird zum Agent-Namen in 42°flow. Gib ihm also einen Namen, den ein Kollege im Canvas versteht — die Regeln aus Kurs 5, Einheit 5 gelten unverändert.
Die gültigen Typen liefert towelscript_catalog (75 Stück, siehe Einheit 5).
Verbindungen
eingang -> extrahieren -> anlegen
-> und => sind gleichwertig. Verzweigungen schreibst du als mehrere Zeilen:
eingang -> pruefen
pruefen -> anlegen
pruefen -> benachrichtigen
Optionen schreiben
Einfache Werte
node a: ai {
model: "gus:tav" // String
timeout: 120 // Zahl
emit_on_error: true // Boolean
}
key: wert und key = wert werden beide akzeptiert — außer bei Objekten, siehe unten. Bleib der Einheitlichkeit halber überall bei :.
Kommentare
Nur //. #, -- und /* */ sind Syntaxfehler.
Strings mit JSON darin
Viele Agents erwarten JSON als String — etwa form_fields beim Web Form Agent. Nutze dafür einfache Anführungszeichen, sonst wird doppelt maskiert und im Agent landet \\\":
node eingang: form {
form_fields: '[{"name":"freitext","label":"E-Mail-Text","type":"textarea","rows":8}]'
}
Mehrzeilige Texte
Für Prompts und Templates gibt es dreifache Anführungszeichen:
node extrahieren: ai {
prompt_template: """Extrahiere den Absender aus dem folgenden Text.
Gib ausschliesslich ein JSON-Objekt zurueck.
Text:
{{ freitext }}"""
}
Backticks funktionieren ebenfalls. '''…''' liefert einen leeren String — sieht aus wie ein Textblock, ist aber keiner.
In einem normalen "…"-String schreibst du echte Zeilenumbrüche direkt hinein; ein \n bleibt literal stehen.
Objekte — hier wird es gefährlich
Für verschachtelte Optionen wie payload oder headers gibt es drei Schreibweisen, die sich sehr ähnlich sehen. Nur eine davon ist richtig.
// ✅ RICHTIG - Doppelpunkt vor der Klammer
payload: {
name: "{{ name }}",
company: "{{ company }}"
}
// ❌ FALSCH - Block ohne Doppelpunkt
payload {
name: "{{ name }}"
}
// ❌ FALSCH - Gleichheitszeichen
payload = { "name": "{{ name }}" }
Was die falschen Varianten anrichten:
| Schreibweise | Was passiert | Meldung? |
|---|---|---|
payload: { … } | Korrektes verschachteltes Objekt | — |
payload { … } | Die Schlüssel landen flach auf oberster Optionsebene — es gibt gar kein payload. Und: alle Verbindungen des Flows verschwinden | keine |
payload = { … } | Die Option wird komplett verworfen | keine |
zwei { … }-Blöcke im selben Node | Syntaxfehler beim zweiten Block | ja |
Das ist der teuerste Fehler im Quelltext. Ein Node mit payload { … } lässt towelscript_check weiterhin valid: true melden — aber die Zeile eingang -> extrahieren -> anlegen wird nicht mehr ausgewertet.
Deployst du das, bekommst du drei Agents, die nebeneinander im Canvas stehen und nichts miteinander zu tun haben. Der Workflow läuft, tut aber nichts.
Deshalb: nach jeder Änderung towelscript_graph aufrufen und prüfen, dass edges nicht leer ist. Mehr dazu in Einheit 6.
Arrays
Arrays von Objekten funktionieren erwartungsgemäß:
node pruefen: filter {
expected_receive_period_in_days: 365
keep_message: true
rules: [
{ type: "regex", value: "@", path: "email" }
]
}
Schlüssel mit Sonderzeichen
Objektschlüssel mit Bindestrich müssen in Anführungszeichen:
headers: {
"x-api-token": "…",
"Content-Type": "application/json"
}
Zugangsdaten
Zugangsdaten gehören nie in den Quelltext. Referenziere sie über @secret(…):
node anlegen: http.post {
post_url: "https://api.example.com/deals"
token: @secret(Pipedrive_token)
}
Der Name ist der, den credential_list liefert — ohne Anführungszeichen. Das ist dieselbe Regel wie in Kurs 5, Einheit 2, nur in Textform.
Ein vollständiges Beispiel
Das ist der Workflow, den du in der nächsten Übung baust:
module academy
flow kontaktanfrage {
node eingang: form {
secret: "academy-kurs6"
form_fields: '[{"name":"freitext","label":"E-Mail-Text einfuegen","type":"textarea","rows":8,"required":true}]'
response_text: "Danke - die Anfrage wird verarbeitet."
}
node extrahieren: ai {
model: "gus:tav"
output_format: "json"
prompt_template: """Extrahiere den Absender aus dem folgenden E-Mail-Text.
Gib ausschliesslich ein JSON-Objekt zurueck, ohne Einleitung und ohne Markdown.
Zielschema:
{"name": "Vor- und Nachname", "company": "Firmenname", "email": "E-Mail-Adresse oder null"}
E-Mail-Text:
{{ freitext }}"""
}
node anlegen: http.post {
post_url: "https://69cde52533a09f831b7c9fa5.mockapi.io/api/v1/contacts"
method: "post"
content_type: "json"
no_merge: true
payload: {
name: "{{ name }}",
company: "{{ company }}",
email: "{{ email }}"
}
}
eingang -> extrahieren -> anlegen
}
Beachte output_format: "json" beim KI-Node: Damit legt der Generative AI Agent die extrahierten Felder direkt auf oberster Payload-Ebene ab — deshalb kann der nächste Node einfach {{ name }} schreiben. Ohne diese Option steht die Antwort als Text unter generation. Die eingehende Payload findest du in beiden Fällen unter last_message.
Zusammengefasst
| Regel | Merksatz |
|---|---|
module zuerst | Sonst kein Compile |
| Flow-Name = Bezeichner | Anzeigename über name beim Deploy |
Nur // als Kommentar | # und /* */ sind Fehler |
| Einfache Quotes für JSON-Strings | Sonst doppelte Maskierung |
"""…""" für Mehrzeiler | '''…''' ergibt leeren String |
payload: { … } mit Doppelpunkt | Ohne Doppelpunkt sind die Verbindungen weg |
@secret(name) für Zugangsdaten | Nie Klartext im Quelltext |
Nach jeder Änderung graph prüfen | edges darf nicht leer sein |